package com.fang.common.util;

import java.util.concurrent.atomic.AtomicLong;

public class CompactSnowflakeIdGenerator {
    // 起始时间戳 (2024-01-01 00:00:00, 秒级)
    private final long twepoch = 1704067200L;

    // 数据中心ID所占位数
    private final long dataCenterIdBits = 4L;

    // 机器ID所占位数
    private final long workerIdBits = 4L;

    // 序列号所占位数
    private final long sequenceBits = 10L;

    // 数据中心ID的最大值
    private final long maxDataCenterId = -1L ^ (-1L << dataCenterIdBits);

    // 机器ID的最大值
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);

    // 时间戳左移位数
    private final long timestampLeftShift = sequenceBits + workerIdBits + dataCenterIdBits;

    // 数据中心ID左移位数
    private final long dataCenterIdShift = sequenceBits + workerIdBits;

    // 机器ID左移位数
    private final long workerIdShift = sequenceBits;

    // 序列号掩码
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);

    // 数据中心ID
    private final long dataCenterId;

    // 机器ID
    private final long workerId;

    // 序列号
    private AtomicLong sequence = new AtomicLong(0L);

    // 上一次生成ID的时间戳
    private volatile long lastTimestamp = -1L;

    public static long getNextId() {
        CompactSnowflakeIdGenerator idGenerator = new CompactSnowflakeIdGenerator(1, 1);
        return idGenerator.nextId();
    }
    public static String getNextIdStr() {
        return CompactSnowflakeIdGenerator.toBase62(getNextId());
    }

    // 构造函数
    public CompactSnowflakeIdGenerator(long workerId, long dataCenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        if (dataCenterId > maxDataCenterId || dataCenterId < 0) {
            throw new IllegalArgumentException(String.format("data center Id can't be greater than %d or less than 0", maxDataCenterId));
        }
        this.workerId = workerId;
        this.dataCenterId = dataCenterId;
    }

    // 生成下一个ID（long类型）
    public synchronized long nextId() {
        long timestamp = timeGen();

        // 如果当前时间小于上一次生成ID的时间戳，说明系统时钟回拨
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }

        // 如果是同一时间生成的，则进行序列号自增
        if (lastTimestamp == timestamp) {
            sequence.set((sequence.get() + 1) & sequenceMask);
            // 序列号溢出，阻塞到下一个时间戳
            if (sequence.get() == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            // 时间戳改变，重置序列号
            sequence.set(0L);
        }

        // 更新上一次生成ID的时间戳
        lastTimestamp = timestamp;

        // 生成ID
        return ((timestamp - twepoch) << timestampLeftShift) |
                (dataCenterId << dataCenterIdShift) |
                (workerId << workerIdShift) |
                sequence.get();
    }

    // 生成下一个ID（String类型，Base62编码）
    public String nextBase62Id() {
        return toBase62(nextId());
    }

    // Base62编码方法
    private static String toBase62(long id) {
        char[] chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray();
        StringBuilder sb = new StringBuilder();
        do {
            sb.append(chars[(int) (id % 62)]);
            id /= 62;
        } while (id > 0);
        return sb.reverse().toString();
    }

    // 阻塞到下一个毫秒
    private long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    // 获取当前时间戳（秒级）
    private long timeGen() {
        return System.currentTimeMillis() / 1000; // 秒级时间戳
    }

    public static void main(String[] args) {
        long nextId = CompactSnowflakeIdGenerator.getNextId();
        String base62 = CompactSnowflakeIdGenerator.toBase62(nextId);
        System.out.println(nextId);
        System.out.println("str base62----> "+ base62);
        System.out.println(CompactSnowflakeIdGenerator.getNextIdStr());
    }
}